package mammuthus.web.service

import java.text.SimpleDateFormat
import java.util
import java.util.Date
import javax.annotation.PostConstruct
import com.alibaba.fastjson.{JSONObject, JSON}
import com.webdm.common.tools.{NumberUtil, CatchUtil}


import mammuthus.web.bean.ShellResult


import mammuthus.web.bean.{AppStoreInfoBean, FileBean, SlaveBean}


import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

import scala.beans.BeanProperty
import scala.collection.JavaConversions._


import net.csdn.modules.transport.{HttpTransportService, DefaultHttpTransportService}
import net.csdn.modules.threadpool.DefaultThreadPoolService
import net.csdn.common.path.Url
import net.csdn.modules.transport.HttpTransportService.SResponse
import net.liftweb.json
import mammuthus.web.action.ActionHelperTrait


/**
 * Created by fengbingjian on 15/5/3 13:30.
 */
@Service
class SlaveService extends AsyncHelperTrait with BasicClusterServiceTrait with AppConfigure with ActionHelperTrait with DockerHostConfigure with DockerContainerConfigure with ConfConfigure with HadoopConfigure with HBaseConfigure{

  val logger = LoggerFactory.getLogger(classOf[SlaveService])
  var master: String = null
  val applicationDir = "/data/1/usr/local/applications"
  val softwareDir = "/data/1/usr/local/softwares"
  var httpClient: HttpTransportService = _
  var shellURL: String = _
  var masterUrl: String = null
  @Autowired
  @BeanProperty
  var settingService: SettingService = null

  @PostConstruct
  def init() = {
    val settings = settingService.getSettings
    master = settings.get("master.host")
    masterUrl = s"http://${master}:9003"
    httpClient = new DefaultHttpTransportService(new DefaultThreadPoolService(settings), settings)
    shellURL = s"${masterUrl}/shell?${authTokenStr}"
  }


  def getMaster: String = master

  def getSlave(): util.List[SlaveBean] = {

    val url = s"${masterUrl}/slaves?${authTokenStr}"
    logger.info("slave url {}", url)
    val src = CatchUtil.getSrc(url, false);
    return JSON.parseArray(src, classOf[SlaveBean])
  }


  def installDeployFile(app: String, appName: String, version: String, slave: String, _url: String, user: String): String = {

    val url = if (_url.endsWith(".git")) _url else s"${masterUrl}/${app}@${version}.tar.gz"

    val installUrl = new Url(s"${masterUrl}/app/install?${authTokenStr}").
      addParam("slaves", encode(s"${slave}&appVersion=${version}")).
      addParam("url", encode(url)).
      addParam("filePath", encode(s"${softwareDir}/${app}@${version}.tar.gz")).
      addParam("installPath", encode(applicationDir)).
      addParam("appName", encode(appName)).
      addParam("app", encode(app)).
      addParam("tt", encode("1800000")
      )

    val rps = httpClient.get(installUrl)
    rps.getContent
  }

  def installDeployConfig(app: String, appName: String, version: String, slave: String, _url: String, user: String, params: Map[String, String]): String = {

    val extraParams = params.filter(f=> ! f._2.isEmpty).map {
      f =>
        s"${f._1}=${encode(f._2)}"
    }.mkString("&")

    val userB = if (user == "-") "" else s"user=${user}"
    val configUrl = s"${masterUrl}/app/config"

    val extraParamsUrl = s"${masterUrl}/app/put/extra"

    logger.info(s"${authTokenStr}${extraParams}")
    val rps = httpClient.get(new Url(extraParamsUrl).query(s"${authTokenStr}appName=${appName}&${extraParams}"))
    logger.info(s"extra params write to app info file: ${rps.getContent}")

    //if (_url.endsWith(".git")) return installResult

    logger.info("install config {}", configUrl)

    val rps2 = httpClient.get(new Url(configUrl).query(s"${authTokenStr}${userB}&appName=${appName}&installPath=${applicationDir}&slaves=${slave}&appVersion=${version}&${extraParams}"))

    rps2.getContent

    //    logger.info("install config result {}", configResult)
    //
    //    configResult
  }

  def install(app: String, version: String, slave: String, _url: String, user: String): String = {


    val dir = applicationDir.replace("applications", "/")

    val userB = if (user == "-") "" else s"user=${user}"
    val configUrl = s"${masterUrl}/app/config?${authTokenStr}${userB}&appName=${app}&installPath=${applicationDir}&slaves=${slave}&appVersion=${version}"


    val url = if (_url.endsWith(".git")) _url else s"${masterUrl}/${app}-${version}.tar.gz"

    val installUrl = s"${masterUrl}/app/install?${authTokenStr}" +
      s"${userB}&slaves=${slave}&appVersion=${version}&" +
      s"url=${url}&" +
      s"filePath=${dir}/softwares/${app}-${version}.tar.gz&" +
      s"installPath=${applicationDir}&" +
      s"appName=${app}&" +
      "tt=1800000"





    logger.info("install  {}", installUrl)

    val installResult = CatchUtil.getSrc(installUrl, false)
    val result = getTaskInfoUntilAllFinish(masterUrl, installResult, 40)

    logger.info(s"install success:${result.filter(f => isSuccess(f)).size} fail:${result.filterNot(f => isSuccess(f)).size}")
    logger.info(s"install result $installResult")

    if (_url.endsWith(".git")) return installResult

    logger.info("install config {}", configUrl)

    val configResult = CatchUtil.getSrc(configUrl, false)

    logger.info("install config result {}", configResult)

    configResult
  }


  def containerList(slaves: String, status: String) = {
    //?slaves=allwefantasy&status=up|exited|exception|all
    val url = s"${masterUrl}/docker/container/list?${authTokenStr}slaves=$slaves&status=$status"
    val src = CatchUtil.getSrc(url, false)
    JSON.parseArray(src)
  }


  def appList(): util.List[JSONObject] = {

    val url = s"${masterUrl}/app/list?${authTokenStr}"
    val src = CatchUtil.getSrc(url, false)
    val json = JSON.parseArray(src).map(l => {

      val obj = l.asInstanceOf[JSONObject]
      obj.put("hosts", obj.getJSONArray("hosts").mkString(","))
      obj
    })
    json

  }

  def appListSimple(): util.List[String] = {

    val url = s"${masterUrl}/app/list?${authTokenStr}"
    val src = CatchUtil.getSrc(url, false)
    val json = JSON.parseArray(src).map(l => {

      val obj = l.asInstanceOf[JSONObject]
      obj.getJSONObject("appInfo").getString("appName")
    })
    json

  }

  def appSlave(app: String): util.List[String] = {

    appSlave(app, true)

  }

  def appSlave(app: String, isHostName: Boolean): util.List[String] = {

    val url = s"${masterUrl}/app/list?${authTokenStr}"

    val src = CatchUtil.getSrc(url, false)
    val res = JSON.parseArray(src).filter(l => {

      val obj = l.asInstanceOf[JSONObject]
      obj.getJSONObject("")
      obj.getJSONObject("appInfo").getString("appName") == app
    })
    if (res.length > 0)
      res.get(0).asInstanceOf[JSONObject].getJSONArray(if (isHostName) "hosts" else "ips").map(l => l.toString)
    else List()

  }


  def getTaskMsg(msg: String): String = {

    if (msg != null && msg.startsWith("[")) {
      val result = JSON.parseArray(msg).getJSONObject(0).getString("msg")
      if (result == null) return "" else return result
    }
    return ""
  }

  def getMethodShell(appName: String, slave: String, action: String, shellCommand: String): String = {


    var taskId: String = null

    var url: String = null
    if ("shell" == action) {
      url = s"${masterUrl}/shell?${authTokenStr}slaves=${slave}&shellCommand=${shellCommand}"

    } else {

      url = s"${masterUrl}/app/control?${authTokenStr}appName=${appName}&command=${action}"
      if (slave != null && "" != slave) url = url + "&slaves=" + slave

    }

    logger.info("shell {}", url)
    val src = CatchUtil.getSrc(url, false)

    taskId = JSON.parseArray(src).getJSONObject(0).getJSONObject("task").getString("taskId")


    val taskUrl = s"${masterUrl}/shell/process?${authTokenStr}taskId=${taskId}&slaves=${slave}"

    logger.info("shell task {}", taskUrl)
    CatchUtil.getSrc(taskUrl, false)
  }

  def getMethodShell(appName: String, slave: String, action: String, shellCommand: String, block: Boolean): String = {


    var taskId: String = null

    var url: String = null
    if ("shell" == action) {
      url = s"${masterUrl}/shell?${authTokenStr}slaves=${slave}&shellCommand=${shellCommand}"

    } else {

      url = s"${masterUrl}/app/control?${authTokenStr}appName=${appName}&command=${action}"
      if (slave != null && "" != slave) url = url + "&slaves=" + slave

    }

    logger.info("shell {}", url)
    val src = CatchUtil.getSrc(url, false)

    taskId = JSON.parseArray(src).getJSONObject(0).getJSONObject("task").getString("taskId")


    val taskUrl = s"${masterUrl}/shell/process?${authTokenStr}taskId=${taskId}&slaves=${slave}"

    logger.info("shell task {}", taskUrl)
    CatchUtil.getSrc(taskUrl, false)
  }

  def shell(appName: String, slave: String, action: String, shellCommand: String): String = {


    var taskId: String = null

    var url: String = null
    if ("shell" == action) {
      url = s"${masterUrl}/shell?${authTokenStr}slaves=${slave}&shellCommand=${shellCommand}"

    } else {

      url = s"${masterUrl}/app/control?${authTokenStr}appName=${appName}&command=${action}"
      if (slave != null && "" != slave) url = url + "&slaves=" + slave

    }

    logger.info("shell {}", url)
    val src = CatchUtil.getSrc(url, false)

    taskId = JSON.parseArray(src).getJSONObject(0).getJSONObject("task").getString("taskId")


    return taskId
  }


  def getTaskLog(taskId: String, slave: String, offset: Int): String = {
    val taskUrl = s"${masterUrl}/shell/process?${authTokenStr}taskId=${taskId}&slaves=${slave}&offset=${offset}"

    logger.info(" task log {}", taskUrl)
    CatchUtil.getSrc(taskUrl, false)
  }

  val dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

  def listDir(appName: String, path: String, host: String): util.List[FileBean] = {

    //http://localhost:9003/app/list/log?appName=zookeeper
    val url = s"${masterUrl}/app/list/log?${authTokenStr}appName=${appName}&filePath=${path}&slaves=${host}"

    logger.info("url {}", url)

    val src = CatchUtil.getSrc(url, false)
    if (src != null && !src.isEmpty && src.startsWith("[")) {

      logger.info("log list result {}", src)

      val files: util.List[FileBean] = JSON.parseArray(src).filter(_.asInstanceOf[JSONObject].getString("hostName") == host).last.asInstanceOf[JSONObject]
        .getJSONArray("files").map(l => {

        val obj = l.asInstanceOf[util.Map[String, Object]]

        val r = new FileBean()

        val d = new Date()
        val time = NumberUtil.getLongValue(obj.get("lastModifyTime"))
        d.setTime(time)

        r.setTimeString(dataFormat.format(d))

        val size = obj.get("size").asInstanceOf[Int]

        if (size >= 1024 * 1024 * 1024) {
          r.setSizeString("%.2f".format(size * 1.0 / (1024 * 1024 * 1024)) + "GB")
        } else if (size >= 1024 * 1024) {
          r.setSizeString("%.2f".format(size * 1.0 / (1024 * 1024)) + "MB")
        } else if (size >= 1024) {
          r.setSizeString("%.2f".format(size * 1.0 / 1024) + "KB")
        } else {
          r.setSizeString(size + "")
        }
        r.setIsDirectory(obj.get("isDirectory").asInstanceOf[Boolean])
        r.setName(obj.get("name").toString)

        r
      }).toList.sortWith{
        (x,y) =>
          (x, y) match{
            case _ if x.isDirectory && y.isDirectory => x.getName.compareTo(y.getName) < 0
            case _ if x.isDirectory && !y.isDirectory => true
            case _ if !x.isDirectory && y.isDirectory => false
            case _  => x.getName.compareTo(x.getName) < 0

          }
      }
      files
    } else {
      new util.ArrayList[FileBean]()
    }
  }


  def logDetail(appName: String, path: String, host: String, offset: Long, size: Int): Tuple3[Long, String, Long] = {

    val url = s"${masterUrl}/app/log?${authTokenStr}appName=${appName}&filePath=${path}&slaves=${host}&offset=${offset}&size=${size}"
    logger.info("logDetail {}", url)
    val src = CatchUtil.getSrc(url, false)
    if (src != null && !src.isEmpty) {
      val obj = JSON.parseArray(src).getJSONObject(0)
      (obj.getLong("offset"), obj.getString("msg"), obj.getLong("fileLen"))
    } else {
      (0, "", 0)
    }
  }

  def gitInstall(app: String, url: String, slave: Array[String]) = {


    val dir = applicationDir


    val installUrl = s"${masterUrl}/app/install?${authTokenStr}" +
      s"slaves=${slave.mkString(",")}&" +
      s"url=${url}&" +
      s"installPath=${dir}&" +
      s"appName=${app}&" +
      "tt=1800000"


    logger.info("install  {}", installUrl)

    val installResult = CatchUtil.getSrc(installUrl, false)
    logger.info("install {}", installResult)

  }


  def appControl(appName: String, slave: String, shellCommand: String, appType: String, moduleFile: String): String = {

    var url = s"${masterUrl}/app/control?${authTokenStr}appName=${appName}&command=${shellCommand}&appType=${appType}&moduleFile=${moduleFile}"
    if (slave != null && "" != slave) url = url + "&slaves=" + slave
    logger.info("shell {}", url)
    val src = CatchUtil.getSrc(url, false)

    return JSON.parseArray(src).getJSONObject(0).getJSONObject("task").getString("taskId")
  }

  def appControl(appName: String, slave: String, shellCommand: String, appType: String, moduleFile: String, block: Boolean): String = {

    var url = s"${masterUrl}/app/control?${authTokenStr}appName=${appName}&command=${shellCommand.replace(" ", "%20")}&appType=${appType}&moduleFile=${moduleFile}&block=${block}"
    if (slave != null && "" != slave) url = url + "&slaves=" + slave
    logger.info("shell {}", url)
    val src = CatchUtil.getSrc(url, false)

    return JSON.parseArray(src).getJSONObject(0).getString("message")
  }


  def checkResponseSuccess(response: SResponse) = {
    response != null && response.getStatus == 200
  }

  def generateShellResult(response: SResponse,timeout:Int=10): ShellResult = {
    if (!checkResponseSuccess(response)) {

      return ShellResult(false, response.getContent, "-", List())
    }
    ShellResult(true, "", response.getContent, getTaskInfoUntilAllFinish(masterUrl, response.getContent, timeout).toList)
  }





  def runDockerContainer(query: String): ShellResult = {
    val runDockerUrl = s"${masterUrl}/docker/run"
    logger.info(s"run docker container: $runDockerUrl " + query)
    val response = httpClient.get(new Url(runDockerUrl).query(authTokenStr + query))
    if (!checkResponseSuccess(response)) {
      return ShellResult(false, response.getContent, "-", List())
    }
    logger.info(s"task detail ${response.getContent}")
    ShellResult(true, "", response.getContent, getTaskInfoUntilAllFinish(masterUrl, response.getContent, 10).toList)
  }

  def enableConf(slaves: String) = {
    val enableConfUrl = new Url(s"${masterUrl}/conf/control").query(s"slaves=${encode(slaves)}")
    logger.info(enableConfUrl.toString)
    httpClient.get(enableConfUrl)

  }

  def getAppStore(): util.List[AppStoreInfoBean] = {

    val url = s"${masterUrl}/app/store?auth_token=f9479ccf304ea4b7c853ef1d3006be67"
    logger.info("app store url {}", url)
    val src = CatchUtil.getSrc(url, false)
    if (src == null || !src.startsWith("[")) return new util.ArrayList[AppStoreInfoBean]()
    else return JSON.parseArray(src, classOf[AppStoreInfoBean])

  }

}
